package com.bolt.admin.config;

import com.bolt.admin.properties.LoginProperties;
import com.bolt.admin.security.shiro.PasswordUtil;
import com.bolt.admin.security.shiro.filter.OnlineSessionFilter;
import com.bolt.admin.security.shiro.filter.SessionFormPasswordFilter;
import com.bolt.admin.security.shiro.filter.SessionLogOutFilter;
import com.bolt.admin.security.shiro.realm.SessionPasswordRealm;
import com.bolt.admin.security.shiro.session.OnlineSessionDAO;
import com.bolt.admin.security.shiro.session.OnlineSessionFactory;
import com.bolt.common.codec.Base64;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by Administrator on 2020/9/18.
 */

@Configuration
@ConditionalOnProperty(prefix = "login.session", name = {"enabled"}, havingValue = "true", matchIfMissing = false)
@EnableConfigurationProperties(LoginProperties.class)
public class ShiroSessionConfig {


    @Bean
    public LoginProperties loginProperties() {
        LoginProperties loginProperties = new LoginProperties();
        return loginProperties;
    }

    /**
     * 自定义Realm
     */
    @Bean
    public SessionPasswordRealm userRealm() {
        SessionPasswordRealm userRealm = new SessionPasswordRealm();
        userRealm.setCredentialsMatcher(new HashedCredentialsMatcher(PasswordUtil.HASH_ALGORITHM));
        return userRealm;
    }

    /**
     * 自定义sessionDAO会话
     */
    @Bean
    public OnlineSessionDAO sessionDAO() {
        OnlineSessionDAO sessionDAO = new OnlineSessionDAO();
        return sessionDAO;
    }

    /**
     * 自定义sessionFactory会话
     */
    @Bean
    public OnlineSessionFactory sessionFactory() {
        OnlineSessionFactory sessionFactory = new OnlineSessionFactory();
        return sessionFactory;
    }

    /**
     * 会话管理器
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        LoginProperties loginProperties = loginProperties();
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        // 删除过期的session
        manager.setDeleteInvalidSessions(true);
        // 设置全局session超时时间
        manager.setGlobalSessionTimeout(loginProperties.getSession().getExpiration() * 60 * 1000);
        // 去掉 JSESSIONID
        manager.setSessionIdUrlRewritingEnabled(false);
        // 是否定时检查session
        manager.setSessionValidationSchedulerEnabled(true);
        // 自定义SessionDao
        manager.setSessionDAO(sessionDAO());
        // 自定义sessionFactory
        manager.setSessionFactory(sessionFactory());
        return manager;
    }

    /**
     * 安全管理器
     */
    @Bean
    public SecurityManager securityManager(SessionPasswordRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(userRealm);
        // 记住我
        securityManager.setRememberMeManager(rememberMeManager());
        // 注入缓存管理器;
        //  securityManager.setCacheManager(getEhCacheManager());
        // session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }


    /**
     * 退出过滤器
     */
    public LogoutFilter logoutFilter() {
        LoginProperties loginProperties = loginProperties();
        SessionLogOutFilter logoutFilter = new SessionLogOutFilter();
        logoutFilter.setLoginUrl(loginProperties.getSession().getLoginUrl());
        return logoutFilter;
    }

    public OnlineSessionFilter onlineSessionFilter() {
        LoginProperties loginProperties = loginProperties();
        OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();
        onlineSessionFilter.setLoginUrl(loginProperties.getSession().getLoginUrl());
        onlineSessionFilter.setOnlineSessionDAO(sessionDAO());
        return onlineSessionFilter;
    }

    public SessionFormPasswordFilter passwordAuthenticationFilter() {
        LoginProperties loginProperties = loginProperties();
        SessionFormPasswordFilter passwordAuthenticationFilter = new SessionFormPasswordFilter();
        passwordAuthenticationFilter.setLoginUrl(loginProperties.getSession().getLoginUrl());
        passwordAuthenticationFilter.setSuccessUrl(loginProperties.getSession().getSuccessUrl());
        passwordAuthenticationFilter.setCaptchaEnabled(loginProperties.getCaptchaCode().isEnabled());
        return passwordAuthenticationFilter;
    }

    /**
     * Shiro过滤器配置
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        LoginProperties loginProperties = loginProperties();
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败，则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl(loginProperties.getSession().getLoginUrl());
        // 权限认证失败，则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl(loginProperties.getSession().getUnauthorizedUrl());

        Map <String, Filter> filters = new LinkedHashMap <String, Filter>();
        filters.put("password", passwordAuthenticationFilter());
        filters.put("onlineSessionFilter", onlineSessionFilter());
        // 注销成功，则跳转到指定页面
        filters.put("logout", logoutFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // Shiro连接约束配置，即过滤链的定义
        LinkedHashMap <String, String> filterChainDefinitionMap = new LinkedHashMap <>();
        // 对静态资源设置匿名访问
        filterChainDefinitionMap.put("/assets/**", "anon");
        filterChainDefinitionMap.put("/common/**", "anon");

        filterChainDefinitionMap.put("/auth/login", "anon");
        filterChainDefinitionMap.put("/test", "anon");
        filterChainDefinitionMap.put(" /swagger-resources", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/v2/api-docs-ext", "anon");
        filterChainDefinitionMap.put("/doc.html", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/admin/index/**", "anon");
        filterChainDefinitionMap.put("/captcha/graph", "anon");

        filterChainDefinitionMap.put("/auth/login", "password");
        filterChainDefinitionMap.put("/admin/**", "onlineSessionFilter");
        // 退出 logout地址，shiro去清除session
        filterChainDefinitionMap.put("/logout", "logout");

        // 所有请求需要认证
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }


    /**
     * cookie 属性设置
     */
    public SimpleCookie rememberMeCookie() {
        LoginProperties loginProperties = loginProperties();
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setDomain(loginProperties.getCookie().getDomain());
        cookie.setPath(loginProperties.getCookie().getPath());
        cookie.setHttpOnly(loginProperties.getCookie().isHttpOnly());
        cookie.setMaxAge(loginProperties.getCookie().getMaxAge() * 24 * 60 * 60);
        return cookie;
    }

    /**
     * 记住我
     */
    public CookieRememberMeManager rememberMeManager() {
        LoginProperties loginProperties = loginProperties();
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode(loginProperties.getCookie().getCipherKey()));
        return cookieRememberMeManager;
    }


    /**
     * Shiro生命周期处理器:
     * 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)
     * 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}



 